
package com.borqs.music;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.text.TextUtils;
import android.widget.RemoteViews;
import android.widget.Toast;

import com.borqs.music.database.DBController;
import com.borqs.music.executor.MusicExecutor;
import com.borqs.music.executor.Task;
import com.borqs.music.executor.TaskAdapter;
import com.borqs.music.executor.TaskListener;
import com.borqs.music.model.DownloadItem;
import com.borqs.music.tasks.DownloadSongTask;
import com.borqs.music.ui.online.OnlineDownloadingListActivity;
import com.borqs.music.util.AccountUtils;
import com.borqs.music.util.AsyncLyricLoader;
import com.borqs.music.util.MusicUtils;
import com.borqs.music.util.MyLogger;
import com.borqs.music.util.SharedPreferencesCompat;

public class DownloadService extends Service implements MusicUtils.DownloadDefs {
    private static final MyLogger logger = MyLogger.getLogger("DownloadService");

    public static final String ACTION_START_DOWNLOAD = "com.borqs.music.ACTION_START_DOWNLOAD";
    public static final String ACTION_TASK_DOWNLOAD_DONE = "com.borqs.music.ACTION_TASK_DOWNLOAD_DONE";
    public static final int NOTIFICATION_DOWNLOAD_STATUS_ID = 1001;

    private ArrayList<DownloadItem> mDownloadItemList;
    private DownloadSongTask mDownloadingTask;
    private boolean mIsDownloading;
    private Notification mNotification;
    private NotificationManager mNotiManager;
    private final String KEY_JSON_FOR_DOWNLOAD = "json_download_for_save";
    private SharedPreferences mPrefs;

    private final String ID = "id";
    private final String TITLE = "title";
    private final String ARTIST = "artist";
    private final String FILENAME = "filename";
    private final String FILEMD5 = "filemd5";
    private final String FAVORITE = "favorite";
    private final String STATUS = "status";

    private final int MSG_SYNC_FAVOR_STATE = 100;
    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SYNC_FAVOR_STATE:
                    syncFavorite(DownloadService.this, (DownloadItem)msg.obj);
                    break;
            }
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mPrefs = getSharedPreferences("Music",
                Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE);
        readFailListFromPrefs();
    }

    private void readFailListFromPrefs() {
        mDownloadItemList = new ArrayList<DownloadItem>();
        String jsonForSync = mPrefs.getString(KEY_JSON_FOR_DOWNLOAD, "");
        if (!TextUtils.isEmpty(jsonForSync)) {
            try {
                JSONArray jArry = new JSONArray(jsonForSync);
                for (int i = 0, len = jArry.length(); i < len; i++) {
                    JSONObject jobj = jArry.getJSONObject(i);
                    DownloadItem item = new DownloadItem(jobj.getInt(ID),
                            jobj.getString(TITLE),jobj.getString(ARTIST),
                            jobj.getString(FILENAME), jobj.getString(FILEMD5),
                            jobj.getBoolean(FAVORITE));
                    item.status = jobj.getInt(STATUS);
                    mDownloadItemList.add(item);
                }
            } catch (JSONException e) {
                logger.e(e.getMessage());
            }
        }
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent == null) {
            return START_STICKY_COMPATIBILITY;
        }
        String action = intent.getAction();
        if (ACTION_START_DOWNLOAD.equals(action)) {
            startNewDownload(intent);
        }
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        saveFailListToPrefs();
        super.onDestroy();
    }

    private void saveFailListToPrefs() {
        // Must clear last record in prefs.
        try {
            JSONStringer jsStringer = new JSONStringer();
            jsStringer.array();
            for (DownloadItem item : mDownloadItemList) {
                if (DOWNLOAD_TASK_FAILED == item.status) {
                    jsStringer.object();
                    jsStringer.key(ID).value(item.getId());
                    jsStringer.key(TITLE).value(item.getTitle());
                    jsStringer.key(ARTIST).value(item.getArtist());
                    jsStringer.key(FILENAME).value(item.getFileName());
                    jsStringer.key(FILEMD5).value(item.getFileMD5());
                    jsStringer.key(STATUS).value(item.status);
                    jsStringer.key(FAVORITE).value(item.isFavorite());
                    jsStringer.endObject();
                }
            }
            jsStringer.endArray();
            Editor ed = mPrefs.edit();
            ed.putString(KEY_JSON_FOR_DOWNLOAD, jsStringer.toString());
            SharedPreferencesCompat.apply(ed);
        } catch (JSONException e) {
            logger.e(e.getMessage());
        }
    }

    private synchronized void startNewDownload(Intent intent) {
        DownloadItem item = intent.getParcelableExtra(DOWNLOAD_INTENT_KEY_SINGLE_ITEM);
        if (null != item) {
            boolean needToast = intent.getBooleanExtra(DOWNLOAD_INTENT_KEY_NEED_SHOW_TOAST, false);
            if (!isAlreadyDownload(item, needToast)) {
                item.status = DOWNLOAD_TASK_WAITING;
                mDownloadItemList.add(item);
            }
        }

        ArrayList<DownloadItem> downloadList = intent
                .getParcelableArrayListExtra(DOWNLOAD_INTENT_KEY_LIST_ITEM);
        if (null != downloadList) {
            int count = 0;
            for (DownloadItem downloadItem : downloadList) {
                if (!isAlreadyDownload(downloadItem, false)) {
                    mDownloadItemList.add(downloadItem);
                    count++;
                }
            }
            Toast.makeText(DownloadService.this,
                    getString(R.string.songs_added_into_download_list, count), Toast.LENGTH_SHORT)
                    .show();
        }

        downloadNextIfExist();
    }

    private boolean isAlreadyDownload(DownloadItem item, boolean needToast) {
        if (mDownloadItemList.contains(item)) {
            DownloadItem i = mDownloadItemList.get(mDownloadItemList.indexOf(item));
            if (i.status == DOWNLOAD_TASK_FAILED) {
                i.status = DOWNLOAD_TASK_WAITING;
                i.value = 0;
                // Retry action
                return true;
            }
            logger.d("Song alread in downloading list, id:" + item.getId());
            if (needToast) {
                Toast.makeText(DownloadService.this,
                        R.string.songs_exist_in_download_list, Toast.LENGTH_SHORT).show();
            }
            return true;
        }


        File file = new File(DownloadSongTask.DOWNLOAD_DIR, item.getFileName());
        if (file.isFile() && file.exists()) {
            logger.d("Song alread exists in local file system, id:" + item.getId());
            if (needToast) {
                Toast.makeText(DownloadService.this,
                        R.string.songs_exist_in_local, Toast.LENGTH_SHORT).show();
            }
            return true;
        }

        return false;
    }

    TaskListener downloadListener = new TaskAdapter() {
        DownloadItem item;

        @Override
        public void onStart(Task task) {
            item = ((DownloadSongTask) task).getDownloadItem();
            item.status = DOWNLOAD_TASK_RUNNING;
            showDownloadNotification(item.getTitle());
        };

        @Override
        public void onCancel(Task task) {
            mDownloadingTask = (DownloadSongTask) task;
            item = mDownloadingTask.getDownloadItem();
            logger.d("onCancel, item.status:" + item.status);
            if (DOWNLOAD_TASK_RUNNING != item.status) {
                return;
            }
            mIsDownloading = false;
            cancelDownloadNotification();
            mDownloadItemList.remove(item);

            mDownloadingTask = null;
            downloadNextIfExist();
        };

        @Override
        public void onDone(Task task) {
            mDownloadingTask = null;
            mIsDownloading = false;
            cancelDownloadNotification();
            item = ((DownloadSongTask) task).getDownloadItem();
            mDownloadItemList.remove(item);
            scanDownloadedFile(item.getFileName());

            if (item.isFavorite()) {
                Message msg = mHandler.obtainMessage(MSG_SYNC_FAVOR_STATE, item);
                mHandler.sendMessageDelayed(msg, 25000);
            }

            Intent i = new Intent(ACTION_TASK_DOWNLOAD_DONE);
            i.putExtra(DOWNLOAD_INTENT_KEY_DOWNLOAD_ITEM, item);
            i.setPackage(getPackageName());
            sendBroadcast(i);

            // Download the corresponding lyric from server.
            String mTicket = AccountUtils.getUserData(DownloadService.this,
                    AccountUtils.BORQS_ACCOUNT_OPTIONS_KEY_SESSION);
            AsyncLyricLoader.getInstance().loadLyricByMd5(mTicket, item.getFileMD5(),
                    item.getTitle(), item.getArtist(), null);
            downloadNextIfExist();
        };

        @Override
        public void onFail(Task task) {
            mDownloadingTask = null;
            mIsDownloading = false;
            cancelDownloadNotification();
            item = ((DownloadSongTask) task).getDownloadItem();
            item.status = DOWNLOAD_TASK_FAILED;
            downloadNextIfExist();
        }

        @Override
        public void onProgress(Task task, long value, long max) {
            item = ((DownloadSongTask) task).getDownloadItem();
            item.max = max;
            item.value = value;
            mNotification.contentView.setTextViewText(R.id.text_notification_percent,
                    (int) (100 * value / max) + "%");
            mNotification.contentView.setProgressBar(R.id.progress, (int) max, (int) value,
                    false);
            mNotiManager.notify(NOTIFICATION_DOWNLOAD_STATUS_ID, mNotification);
            if (value >= max) {
                cancelDownloadNotification();
            }
        };
    };

    private void scanDownloadedFile(String fileName) {
        Intent scanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
        //TODO some songs can not be scanned successfully.
        scanIntent.setData(Uri.parse("file://" + new File(DownloadSongTask.DOWNLOAD_DIR, fileName).getPath()));
        sendBroadcast(scanIntent);
    }

    private synchronized void downloadNextIfExist() {
        if (mDownloadItemList.size() > 0 && !mIsDownloading) {
            for (DownloadItem item : mDownloadItemList) {
                if (item.status == DOWNLOAD_TASK_WAITING) {
                    mIsDownloading = true;
                    item.status = DOWNLOAD_TASK_RUNNING;
                    String mTicket = AccountUtils.getUserData(this,
                            AccountUtils.BORQS_ACCOUNT_OPTIONS_KEY_SESSION);
                    mDownloadingTask = new DownloadSongTask(downloadListener, mTicket, item);
                    MusicExecutor exec = MusicExecutor.getInstance();
                    exec.execute(mDownloadingTask);
                    logger.d("START:" + item.getId() + ", fileName:" + item.getFileName());
                    break;
                }
            }
        }

        if (!mIsDownloading) {
            stopSelf();
        }
    }

    private void showDownloadNotification(String fileName) {
        mNotiManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        RemoteViews views = new RemoteViews(getPackageName(), R.layout.notification);
        views.setImageViewResource(R.id.icon_notification, R.drawable.list_music_download);
        views.setTextViewText(R.id.text_notification, fileName);
        views.setTextViewText(R.id.text_notification_percent, "0%");
        Intent statusintent = new Intent(this, OnlineDownloadingListActivity.class);
        statusintent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        mNotification = new Notification();
        mNotification.contentView = views;
        mNotification.flags |= Notification.FLAG_ONGOING_EVENT;
        mNotification.icon = R.drawable.list_music_download;
        mNotification.contentIntent = PendingIntent.getActivity(this, 0, statusintent, 0);
        mNotiManager.notify(NOTIFICATION_DOWNLOAD_STATUS_ID, mNotification);
    }

    private void cancelDownloadNotification() {
        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        nm.cancel(NOTIFICATION_DOWNLOAD_STATUS_ID);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return serviceBinder;
    }

    private ServiceBinder serviceBinder = new ServiceBinder();

    public class ServiceBinder extends Binder implements IDownloadService {

        @Override
        public List<DownloadItem> getDownloadingTaskList() {
            return mDownloadItemList;
        }

        @Override
        public boolean cancelDownloadTask(DownloadItem item) {
            if (null != item) {
                if ((null != mDownloadingTask)
                        && (item.equals(mDownloadingTask.getDownloadItem()))) {
                    mDownloadingTask.cancel();
                } else {
                    item.status = DOWNLOAD_TASK_CANCEL;
                }
                mDownloadItemList.remove(item);
            }
            return true;
        }

        @Override
        public boolean isDownloading() {
            return mIsDownloading;
        }

        @Override
        public boolean startNewDownloadTask(DownloadItem item) {
            if (null != item) {
                if (!isAlreadyDownload(item, false)) {
                    logger.d("item.getFileName():" + item.getFileName());
                    item.status = DOWNLOAD_TASK_WAITING;
                    mDownloadItemList.add(item);
                }
            }
            downloadNextIfExist();
            return true;
        }

    }

    /**
     * Sync online song favorite state to downloaded local song.
     */
    private void syncFavorite(Context context, DownloadItem item) {
        /*SongInfo song = new SongInfo();
        song.id = item.getId();
        song.artist = item.getArtist();
        song.title = item.getTitle();
        song.fileName = item.getFileName();
        song.isFavorite = item.isFavorite();
        CacheUtils.SongCache.put(context,CacheUtils.CACHE_TYPE_SYNC_FAVOR, song);

        CacheUtils.syncLocalFavoriteSong(context);*/
        List<Long> favorIDs = MusicUtils.loadFavoriteSongIds(this);
        long localSongID = DBController.getInstance(this).findSongIdByMD5(item.getFileMD5());
        if (localSongID > 0 && !favorIDs.contains(localSongID)) {
            favorIDs.add(localSongID);
            MusicUtils.saveFavoriteQueue(this, favorIDs);
        }
    }
}
